// pkg/pluginmgr/manager.go
package pluginmgr

import (
	"context"
	"fmt"
	"github.com/hashicorp/go-hclog"
	"log"
	"os"
	"os/exec"
	"platform/proto/plugin" // 我们生成的 protobuf 包
	"sync"
	"time"

	goPlugin "github.com/hashicorp/go-plugin" // 使用别名避免冲突
	"google.golang.org/grpc"
)

// PluginClient 封装插件客户端
type PluginClient struct {
	Client     *goPlugin.Client    // 使用别名的 go-plugin 客户端
	GRPCClient plugin.PluginClient // 使用生成的 PluginClient 接口
	Info       *plugin.PluginInfo
}

type PluginManager struct {
	mu          sync.RWMutex
	plugins     map[string]*PluginClient
	pluginPaths map[string]string
}

func NewPluginManager() *PluginManager {
	return &PluginManager{
		plugins:     make(map[string]*PluginClient),
		pluginPaths: make(map[string]string),
	}
}

// PluginGRPC 实现 go-plugin 的 GRPCPlugin 接口
type PluginGRPC struct {
	goPlugin.Plugin // 使用别名的 Plugin 接口
}

func (p *PluginGRPC) GRPCServer(broker *goPlugin.GRPCBroker, s *grpc.Server) error {
	// 在网关端，我们主要是客户端，所以这里不需要实现服务端
	return nil
}

func (p *PluginGRPC) GRPCClient(ctx context.Context, broker *goPlugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
	// 使用生成的 NewPluginClient 函数创建客户端
	return plugin.NewPluginClient(c), nil
}

// LoadPlugin 加载插件
func (pm *PluginManager) LoadPlugin(name, pluginPath string) error {
	pm.mu.Lock()
	defer pm.mu.Unlock()

	if _, exists := pm.plugins[name]; exists {
		return fmt.Errorf("plugin %s already loaded", name)
	}

	// 创建 go-plugin 客户端
	client := goPlugin.NewClient(&goPlugin.ClientConfig{
		HandshakeConfig: goPlugin.HandshakeConfig{
			ProtocolVersion:  1,
			MagicCookieKey:   "PLATFORM_PLUGIN",
			MagicCookieValue: "zhitu-platform",
		},
		Plugins: map[string]goPlugin.Plugin{
			"plugin": &PluginGRPC{},
		},
		Cmd:              exec.Command(pluginPath),
		AllowedProtocols: []goPlugin.Protocol{goPlugin.ProtocolGRPC},
		AutoMTLS:         true,
		Logger: hclog.New(&hclog.LoggerOptions{
			Name:   "plugin-" + name, // 使用插件名称
			Level:  hclog.Info,
			Output: os.Stdout,
		}),
	})

	// 连接到插件
	rpcClient, err := client.Client()
	if err != nil {
		client.Kill()
		return fmt.Errorf("failed to connect to plugin: %w", err)
	}

	// 获取插件服务
	raw, err := rpcClient.Dispense("plugin")
	if err != nil {
		client.Kill()
		return fmt.Errorf("failed to dispense plugin: %w", err)
	}

	// 类型断言获取 gRPC 客户端
	grpcClient, ok := raw.(plugin.PluginClient)
	if !ok {
		client.Kill()
		return fmt.Errorf("dispensed object is not a PluginClient, got: %T", raw)
	}

	// 获取插件信息
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	info, err := grpcClient.GetInfo(ctx, &plugin.Empty{})
	if err != nil {
		client.Kill()
		return fmt.Errorf("failed to get plugin info: %w", err)
	}

	// 存储插件
	pm.plugins[name] = &PluginClient{
		Client:     client,
		GRPCClient: grpcClient,
		Info:       info,
	}
	pm.pluginPaths[name] = pluginPath

	log.Printf("Plugin %s loaded successfully: %s", name, info.Description)
	return nil
}

// UnloadPlugin 卸载插件
func (pm *PluginManager) UnloadPlugin(name string) error {
	pm.mu.Lock()
	defer pm.mu.Unlock()

	client, exists := pm.plugins[name]
	if !exists {
		return fmt.Errorf("plugin %s not found", name)
	}

	client.Client.Kill()
	delete(pm.plugins, name)
	delete(pm.pluginPaths, name)

	log.Printf("Plugin %s unloaded successfully", name)
	return nil
}

// CallPlugin 调用插件
func (pm *PluginManager) CallPlugin(ctx context.Context, name string, req *plugin.Request) (*plugin.Response, error) {
	pm.mu.RLock()
	client, exists := pm.plugins[name]
	pm.mu.RUnlock()

	if !exists {
		return nil, fmt.Errorf("plugin %s not found", name)
	}

	return client.GRPCClient.Handle(ctx, req)
}

// GetPluginInfo 获取插件信息
func (pm *PluginManager) GetPluginInfo(name string) (*plugin.PluginInfo, error) {
	pm.mu.RLock()
	defer pm.mu.RUnlock()

	client, exists := pm.plugins[name]
	if !exists {
		return nil, fmt.Errorf("plugin %s not found", name)
	}

	return client.Info, nil
}

// ListPlugins 列出所有插件
func (pm *PluginManager) ListPlugins() []string {
	pm.mu.RLock()
	defer pm.mu.RUnlock()

	plugins := make([]string, 0, len(pm.plugins))
	for name := range pm.plugins {
		plugins = append(plugins, name)
	}
	return plugins
}

// HealthCheck 健康检查
func (pm *PluginManager) HealthCheck(ctx context.Context) map[string]bool {
	pm.mu.RLock()
	defer pm.mu.RUnlock()

	results := make(map[string]bool)
	for name, client := range pm.plugins {
		health, err := client.GRPCClient.HealthCheck(ctx, &plugin.Empty{})
		if err != nil {
			results[name] = false
		} else {
			results[name] = health.Healthy
		}
	}
	return results
}
